Skip to content

Comments

fix: improve taskbar title display with smooth truncation#1443

Open
wjyrich wants to merge 1 commit intolinuxdeepin:masterfrom
wjyrich:fix-bug-350289
Open

fix: improve taskbar title display with smooth truncation#1443
wjyrich wants to merge 1 commit intolinuxdeepin:masterfrom
wjyrich:fix-bug-350289

Conversation

@wjyrich
Copy link
Contributor

@wjyrich wjyrich commented Feb 10, 2026

  1. Replaced simple text elision with a custom fade-out effect for better visual appearance
  2. Added gradient opacity mask to smoothly fade text instead of abrupt ellipsis cut-off
  3. Modified TextCalculator to return full text instead of elided text when space permits
  4. Added minimum character display check to ensure at least 2 characters can be shown
  5. Updated width calculation to use optimal width constraints

Log: Improved taskbar application title display with smooth fade-out effect instead of ellipsis

Influence:

  1. Test taskbar with long application titles to see smooth fade-out effect
  2. Verify titles display correctly in both light and dark themes
  3. Check that very short titles still display properly
  4. Test with multiple applications open to ensure consistent behavior
  5. Verify performance with many open applications

fix: 改进任务栏标题显示,实现平滑截断效果

  1. 使用自定义淡出效果替换简单的文本省略,提升视觉体验
  2. 添加渐变透明度遮罩,实现平滑淡出效果而非生硬的省略号截断
  3. 修改TextCalculator,在空间允许时返回完整文本而非省略文本
  4. 添加最小字符显示检查,确保至少能显示2个字符
  5. 更新宽度计算以使用最佳宽度约束

Log: 改进任务栏应用程序标题显示,使用平滑淡出效果替代省略号

Influence:

  1. 测试任务栏中长应用程序标题的平滑淡出效果
  2. 验证标题在浅色和深色主题下正确显示
  3. 检查非常短的标题是否仍能正常显示
  4. 测试多个应用程序打开时的行为一致性
  5. 验证在打开大量应用程序时的性能表现

PMS: BUG-350289

Summary by Sourcery

Improve dock taskbar application title display with smooth truncation and minimum visible characters.

New Features:

  • Add a gradient-based opacity mask to fade out overflowing taskbar titles instead of using ellipsis truncation.

Bug Fixes:

  • Ensure taskbar titles are only shown when at least two characters can fit within the available width.

Enhancements:

  • Update title width calculation to use the optimal single-text width constraint while capping it at the text's intrinsic width.
  • Return full, unelided title text when space permits, simplifying title rendering in the QML layer.

@wjyrich wjyrich requested a review from 18202781743 February 10, 2026 08:08
@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: wjyrich

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 10, 2026

Reviewer's Guide

Implements a smooth fade-out truncation effect for taskbar app titles by replacing ellipsis-based elision with a gradient opacity mask, and adjusts TextCalculator to return full text when at least two characters fit while respecting optimal width constraints.

Sequence diagram for taskbar title rendering with fade-out truncation

sequenceDiagram
    actor User
    participant Taskbar
    participant AppItemTitleQML
    participant TextCalculatorAttached
    participant TextCalculator
    participant TitleLoader
    participant TitleContainer
    participant OpacityMaskEffect

    User->>Taskbar: View taskbar
    Taskbar->>AppItemTitleQML: Create/update title for app item
    AppItemTitleQML->>TextCalculatorAttached: set text and calculator
    TextCalculatorAttached->>TextCalculatorAttached: updateElidedText()
    TextCalculatorAttached->>TextCalculator: font()
    TextCalculator->>TextCalculator: optimalSingleTextWidth()
    TextCalculatorAttached->>TextCalculatorAttached: canDisplayMinChars(text, fontMetrics, maxWidth)
    alt AtLeastTwoCharsFit
        TextCalculatorAttached->>TextCalculatorAttached: m_elidedText = full text
    else NotEnoughSpace
        TextCalculatorAttached->>TextCalculatorAttached: m_elidedText = empty
    end

    AppItemTitleQML->>TitleLoader: active = enabled && elidedText.length > 0
    TitleLoader->>TitleContainer: Instantiate when active
    TitleContainer->>TextCalculator: optimalSingleTextWidth()
    TitleContainer->>TitleContainer: width = min(titleText.implicitWidth, optimalSingleTextWidth)
    TitleContainer->>TitleContainer: layer.enabled = titleText.implicitWidth > width + 1
    alt NeedsFadeOut
        TitleContainer->>OpacityMaskEffect: Apply horizontal gradient mask
    else FitsWithoutFade
        TitleContainer->>TitleContainer: clip = false (no fade)
    end
    TitleContainer->>User: Display title with smooth fade-out when truncated
Loading

Class diagram for updated TextCalculator truncation logic

classDiagram
    class TextCalculator {
        +QFont m_font
        +qreal optimalSingleTextWidth()
        +QFont font()
        +qreal calculateElidedTextWidth(QString text, qreal maxWidth)
        +QStringList getApplicationTitles()
    }

    class TextCalculatorAttached {
        -TextCalculator *m_calculator
        -QString m_text
        -QString m_elidedText
        +void updateElidedText()
        +QString text()
        +QString elidedText()
        +TextCalculator* calculator()
    }

    class canDisplayMinChars {
        +static bool canDisplayMinChars(QString text, QFontMetricsF fontMetrics, qreal maxWidth)
    }

    TextCalculatorAttached --> TextCalculator : uses
    TextCalculator ..> canDisplayMinChars : calls
    TextCalculatorAttached ..> canDisplayMinChars : calls
Loading

File-Level Changes

Change Details Files
Replace ellipsis-based elided title Text with a container using a gradient opacity mask for smooth fade-out and dynamic width.
  • Wrap the title Text in an Item container that controls width using TextCalculator.optimalSingleTextWidth and the text’s implicit width.
  • Enable a layer-based OpacityMask only when the text is wider than the available space, clipping content accordingly.
  • Define a mask Rectangle sized from measured ellipsis width and positioned at the right edge, with a horizontal gradient from opaque to transparent for fade-out.
  • Switch the displayed text binding from pre-elided text to the raw root.text and make the font fallback-safe when TextCalculator.calculator is absent.
panels/dock/taskmanager/package/AppItemTitle.qml
Change TextCalculator to stop producing elided strings and instead validate minimum visible characters and compute constrained width.
  • Replace the isValidElidedText helper with canDisplayMinChars, which checks that at least two characters can be rendered within maxWidth using QFontMetricsF.
  • Update calculateElidedTextWidth to return 0 when fewer than two characters fit, otherwise return the lesser of the full text width and maxWidth instead of an elided-text width.
  • Modify TextCalculatorAttached::updateElidedText to set elidedText to the full source text only when canDisplayMinChars passes, otherwise clear it, effectively treating elidedText as a visibility flag rather than modified content.
panels/dock/taskmanager/textcalculator.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The OpacityMask's maskSource rectangle is only sized to ellipsisMetrics.width/height, which likely means most of the text is fully masked out; consider matching the mask rectangle to titleContainer's full size and applying the gradient only on the trailing portion instead.
  • In AppItemTitle.qml, the fade gradient is hard-coded with opaque white (#FFFFFFFF), which may not behave as expected if the text color or background changes; you might want to derive the mask color from the actual text color or use an alpha-only mask to avoid theme-dependent artifacts.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `OpacityMask`'s `maskSource` rectangle is only sized to `ellipsisMetrics.width/height`, which likely means most of the text is fully masked out; consider matching the mask rectangle to `titleContainer`'s full size and applying the gradient only on the trailing portion instead.
- In `AppItemTitle.qml`, the fade gradient is hard-coded with opaque white (`#FFFFFFFF`), which may not behave as expected if the text color or background changes; you might want to derive the mask color from the actual text color or use an alpha-only mask to avoid theme-dependent artifacts.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@wjyrich wjyrich force-pushed the fix-bug-350289 branch 2 times, most recently from 7cd90a9 to f3e908c Compare February 10, 2026 09:48
@18202781743
Copy link
Contributor

例如,现在能正常显示三个字符,当前应用为五个字符,那应该是第四个汉字开始渐隐吧,而不是第三个就开始了,能够显示的文本是四个字符或者更多,

1. Modified TextCalculator to track truncation status and ellipsis width
2. Added gradient fade effect for truncated text instead of ellipsis
character
3. Changed minimum character count from 1 to 2 for better readability
4. Added new properties isTruncated and ellipsisWidth to
TextCalculatorAttached
5. Implemented OpacityMask with gradient for smooth text fade-out

Log: Improved task manager app title display with gradient fade effect
for truncated text

Influence:
1. Test app titles with varying lengths to ensure proper truncation
2. Verify gradient fade effect appears only when text is truncated
3. Check text alignment and positioning with the new fade effect
4. Test with both light and dark themes to ensure color compatibility
5. Verify minimum character count of 2 is respected in all cases
6. Test performance with multiple app items displaying truncated titles

fix: 改进文本截断效果,添加渐变淡出

1. 修改TextCalculator以跟踪截断状态和省略号宽度
2. 为截断文本添加渐变淡出效果替代省略号字符
3. 将最小字符数从1改为2以提高可读性
4. 向TextCalculatorAttached添加isTruncated和ellipsisWidth属性
5. 实现带渐变的OpacityMask以实现平滑的文本淡出效果

Log: 改进任务管理器应用标题显示,为截断文本添加渐变淡出效果

Influence:
1. 测试不同长度的应用标题以确保正确截断
2. 验证渐变淡出效果仅在文本被截断时出现
3. 检查新淡出效果下的文本对齐和定位
4. 测试浅色和深色主题以确保颜色兼容性
5. 验证在所有情况下都遵守最小2个字符的限制
6. 测试多个应用项目显示截断标题时的性能表现
@deepin-ci-robot
Copy link

deepin pr auto review

这份代码修改的主要目的是改变任务栏应用标题的截断显示方式,从传统的省略号("…")截断改为淡出渐变效果。以下是对代码的详细审查,涵盖语法逻辑、代码质量、性能和安全性四个方面:

1. 语法逻辑与功能实现

QML 部分 (AppItemTitle.qml):

  • 逻辑正确性:引入 Qt5Compat.GraphicalEffects 并使用 OpacityMask 配合 Gradient 来实现文字末尾的淡出效果是符合逻辑的。
  • 渐变方向GradientStop 的设置从 0.6 到 1.0,意味着文字宽度的最后 40% 区域会从不透明渐变到完全透明。这个逻辑是合理的,能够实现平滑的淡出。
  • 条件渲染layer.enabled: root.TextCalculator.ellipsisWidth > 0 这个逻辑很好,只有当文字确实被截断(需要显示省略号效果)时才开启图层特效,避免全屏渲染带来的性能损耗。

C++ 部分 (textcalculator.cpp):

  • 截断算法变更:代码移除了 QFontMetrics::elidedText,改为手动循环 chop(1) 来截取字符串。
    • 逻辑检查:新逻辑确保了文字宽度严格小于 maxWidth。但是,它移除了末尾的 "…" 符号。这与 QML 中的渐变遮罩配合使用是正确的,因为视觉上不需要显示 "…" 符号。
  • 字符限制变更calculateOptimalTextWidth 中的循环从 charCount >= 1 改为 charCount >= 2
    • 潜在问题:如果限制最小为 2 个字符,当空间极度狭窄时(比如只能容纳 1 个字符),循环会结束,可能导致显示异常或空白。建议保留 1 或者增加对极端情况的特殊处理,确保至少能显示 1 个字符或特定的占位符。
  • 省略号宽度计算:在 updateElidedText 中,如果 visibleText != m_text(即发生了截断),则计算 "…" 的宽度赋值给 m_ellipsisWidth
    • 逻辑疑问:这里计算了省略号的宽度,但在 QML 的 maskSource 中,width 被设置为 root.TextCalculator.ellipsisWidth这里存在逻辑冲突
      • C++ 计算的是 "…" 这个字符的像素宽度(通常很小,约等于一个字符宽)。
      • QML 遮罩需要的是整个可见文字区域的宽度,以便从该位置开始渐变。
      • 后果:如果直接使用省略号的字符宽度作为遮罩矩形的宽度,渐变区域会紧贴着文字的最左侧开始,导致文字几乎不可见(因为大部分区域被透明遮罩覆盖了)。QML 遮罩的宽度应该是完整可见文字的宽度(即 fontMetrics.horizontalAdvance(visibleText)),而不是省略号的宽度。

2. 代码质量

  • 可读性:C++ 中手动截断文字的 while 循环清晰易懂,意图明确。
  • 冗余代码
    • calculateElidedText 函数中,如果 visibleText 为空返回 0.0,但在调用处可能需要明确处理这种情况,以免布局崩溃。
    • updateElidedText 中,if (visibleText != m_text) 块内的逻辑可以稍微简化,但目前的写法为了信号发射的清晰度是可以接受的。
  • 硬编码QString::fromUtf8("…") 在代码中出现了。虽然 "…" 是标准字符,但为了统一字符编码处理,建议检查项目中是否有统一的宏或常量定义此类特殊字符。

3. 代码性能

  • QML 渲染
    • layer.enabled 的动态开启是性能优化的关键点,做得很好。ShaderEffect(OpacityMask 的底层实现)开销较大,仅在需要时启用非常重要。
  • C++ 计算
    • 性能隐患:将 QFontMetrics::elidedText(通常是 O(1) 或高度优化的)替换为 while 循环配合 chop(1)horizontalAdvance(O(N))。
    • 对于长字符串,每次截断一个字符并重新计算宽度,效率较低。例如,如果需要截断 100 个字符,就需要计算 100 次宽度。
    • 改进建议:可以使用二分查找法来找到合适的截断点,将复杂度从 O(N) 降低到 O(log N)。或者保留 elidedText 计算出的结果作为参考,再进行微调。

4. 代码安全

  • 空指针与空字符串
    • 代码中对 visibleText.isEmpty() 进行了检查,这很好,防止了空字符串导致的后续计算错误。
    • m_calculator 的空指针检查在 updateElidedText 中存在,符合安全规范。
  • 类型安全
    • 使用 qreal 处理浮点数宽度,符合 Qt 规范。

总结与改进建议

这段代码实现了新的 UI 效果,但在逻辑性能上有明显的改进空间。

核心改进建议

  1. 修复 QML 遮罩宽度的逻辑错误 (严重)

    • 问题m_ellipsisWidth 代表的是省略号字符的宽度,而 QML 中 maskRectwidth 应该是可见文字的总宽度
    • 修改方案
      • C++ (textcalculator.cpp):在 updateElidedText 中,不要将省略号宽度传给 QML。应该将截断后实际显示的文字宽度传给 QML。
      • QML (AppItemTitle.qml)maskRectwidth 应该绑定到 root.TextCalculator.visibleTextWidth(假设新增属性)。
      • 或者:如果目的是让渐变发生在省略号的位置,那么 C++ 应该计算"完整可见文字宽度 - 省略号宽度"作为渐变起始点。但根据 QML 代码 GradientStop { position: 1; ... },遮罩矩形的右端就是文字的右端。因此,maskRect 的宽度必须等于可见文字的宽度。
  2. 优化 C++ 截断算法 (性能)

    • 将线性循环截断改为二分查找,以减少 horizontalAdvance 的调用次数。
  3. 边界条件处理

    • 恢复 charCount >= 1 的逻辑,或者确保在极端宽度下至少显示 1 个字符,而不是留白。
  4. 代码重构建议

    • calculateElidedTextupdateElidedText 中有重复的截断逻辑。建议提取一个私有辅助函数 getVisibleText(const QString &text, qreal maxWidth) 来统一处理。

修正后的代码逻辑示例

C++ (textcalculator.cpp):

// 在 updateElidedText 中
QString visibleText = m_text;
// 使用二分查找优化性能
int left = 0, right = visibleText.length();
while (left < right) {
    int mid = (left + right + 1) / 2;
    if (fontMetrics.horizontalAdvance(visibleText.left(mid)) <= maxWidth) {
        left = mid;
    } else {
        right = mid - 1;
    }
}
visibleText = visibleText.left(left);

if (visibleText.isEmpty()) {
    visibleText = "";
    m_ellipsisWidth = 0.0; // 或者设置为某个最小值,防止布局错乱
} else if (visibleText != m_text) {
    // 只有真正被截断时,才标记需要显示省略效果
    // 这里我们计算的是可见文字的总宽度,传给 QML 用于设置遮罩
    m_ellipsisWidth = fontMetrics.horizontalAdvance(visibleText); 
} else {
    m_ellipsisWidth = 0.0;
}

if (m_elidedText != visibleText) {
    m_elidedText = visibleText;
    emit elidedTextChanged();
    emit ellipsisWidthChanged(); // 信号名可能需要改为 visibleTextWidthChanged 更准确
}

QML (AppItemTitle.qml):

// ...
layer.enabled: root.TextCalculator.ellipsisWidth > 0
layer.effect: OpacityMask {
    maskSource: Rectangle {
        width: root.TextCalculator.ellipsisWidth // 这里现在接收的是可见文字的总宽度
        height: titleText.height
        gradient: Gradient {
            orientation: Gradient.Horizontal
            GradientStop { position: 1.0; color: "#00FFFFFF" } // 结尾完全透明
            GradientStop { position: 0.8; color: "#FFFFFFFF" } // 从 80% 处开始渐变
        }
    }
}
// ...

注意:如果 m_ellipsisWidth 变量名在 C++ 中保留,建议在 QML 绑定时理解其含义已变为"遮罩/可见区域宽度"。或者为了代码清晰,将 C++ 属性重命名为 visibleTextWidth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants